#include "can.h"

#include <cstring>
#include <iostream>

/**
 * 初始化CAN接口
 *
 * @param name CAN接口名称，例如"can0"
 *
 * @return 返回CAN套接字描述符，失败时返回-1
 *
 * 此函数负责创建一个CAN通信接口的套接字，并将其绑定到指定的CAN接口上
 * 它首先创建一个CAN协议的原始套接字，然后获取指定接口的索引，并将套接字绑定到该接口
 * 如果在任何步骤中遇到错误，函数将输出错误信息并返回-1
 */
Can::Can(const char* name) {
    // 创建CAN协议的原始套接字
    can_fd_ = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    // 如果创建套接字失败，输出错误信息并返回-1
    if (can_fd_ < 0) {
        std::cerr << "Error opening socket" << std::endl;
        return;
    }

    // 初始化接口请求结构体
    struct ifreq ifr;
    strcpy(ifr.ifr_name, name);
    // 获取指定接口的索引
    ioctl(can_fd_, SIOCGIFINDEX, &ifr);

    // 初始化CAN地址结构体
    struct sockaddr_can addr;
    addr.can_family  = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    // 将套接字绑定到指定的CAN接口
    if (bind(can_fd_, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
        std::cerr << "Error binding socket to interface" << std::endl;
        return;
    }
}

/**
 * 发送CAN命令
 *
 * @param cob_id CAN报文的标识符
 * @param data 指向数据的指针，数据将被发送
 * @param data_len 数据长度，表示发送数据的字节数
 *
 * 此函数负责组装CAN报文，并通过指定的CAN套接字发送出去
 */
void Can::SendCommand(uint32_t cob_id, const uint8_t* data, size_t data_len) {
    // 创建CAN报文结构体
    struct can_frame frame;
    // 设置CAN报文的标识符
    frame.can_id = cob_id;
    // 设置CAN报文的数据长度
    frame.can_dlc = data_len;
    // 将数据复制到CAN报文的数据字段中
    for (size_t i = 0; i < data_len; ++i) {
        frame.data[i] = data[i];
    }

    // 发送CAN报文，如果发送失败，则输出错误信息
    if (write(can_fd_, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
        std::cerr << "Error sending CAN frame" << std::endl;
    }
}

/**
 * CAN类的ReceiveCommand方法，用于接收CAN命令帧。
 *
 * 此方法通过读取CAN设备文件描述符can_fd_来接收CAN命令帧。
 * 它期望接收到一个完整结构的can_frame，如果接收到的字节数
 * 与预期不符，则视为接收错误，并输出错误信息到标准错误流。
 *
 * @param frame 指向CAN帧结构的指针，通过此参数接收CAN帧数据。
 * @return 如果成功接收到一个完整结构的CAN帧，则返回true；
 *          否则返回false，表示接收失败。
 */
bool Can::ReceiveCommand(struct can_frame* frame) {
    // 从CAN设备读取一帧数据，期望读取到一个完整结构的can_frame
    int n_bytes = read(can_fd_, frame, sizeof(struct can_frame));
    // 检查接收到的数据长度是否符合预期
    if (n_bytes != sizeof(struct can_frame)) {
        std::cerr << "error receiving CAN frame" << std::endl;
        return false;
    }

    return true;
}